﻿#region Library
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Text.RegularExpressions;
using System.Threading;

#endregion

namespace SmsControl
{
    /// <summary>
    /// This class is used for sending SMS using AT Command
    /// </summary>
    public class SmsControl
    {
        //====================================================================================
        #region Attributes
        SerialPort port;
        string modemName;//"AT+CGMI\r\r\n WAVECOM MODEM\r\n\r\nOK"
        /// <summary>
        /// Gets the name of the modem.
        /// </summary>
        /// <value>
        /// Modem name.
        /// </value>
        public string ModemName
        {
            get
            {
                return modemName;
            }
        }
        string simSerie;//"AT+CCID\r\r\n+CCID: \"8984072100067334530\"\r\n\r\nOK"
        /// <summary>
        /// Gets the serie number of the sim.
        /// </summary>
        /// <value>
        /// Sim serie number.
        /// </value>
        public string SimSerie
        {
            get
            {
                return simSerie;
            }
        }
        string portName;
        /// <summary>
        /// Gets the name of the port that is connected.
        /// </summary>
        /// <value>
        /// The name of the port.
        /// </value>
        public string PortName
        {
            get
            {
                return portName;
            }

        }
        bool listening = true;
        /// <summary>
        /// Gets or sets a value that determines whether to be alert when receiving a new message.
        /// </summary>
        /// <value>
        ///   <c>true</c> if listening; otherwise, <c>false</c>.
        /// </value>
        public bool Listening
        {
            get { return listening; }
            set { listening = value; }
        }
        bool canCheckBalance = true;
        SeperateString seperateString = new SeperateString();
        
        #endregion

        //====================================================================================
        #region General Method
        /// <summary>
        /// Initializes a new instance using the specified port name.
        /// </summary>
        /// <param name="portName">Name of the port.</param>
        public bool Initialize(string portName)
        {
            try
            {
                //port = new SerialPort(portName, 115200, Parity.None, 8, StopBits.One);

                port = new SerialPort(portName, 9600, Parity.None, 8, StopBits.One);
                port.ReadTimeout = 1000;
                port.WriteTimeout = 1000;
                port.DtrEnable = true;
                port.RtsEnable = true;

                port.Open();
                if (!port.IsOpen)
                {
                    return false;
                }
                ConfigureModem();
                StartListener();
                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// Sends the message to the phone number.
        /// </summary>
        /// <param name="number">The phone number that you want to send to.</param>
        /// <param name="message">The message that you want to send.</param>
        /// <returns>true if this message is sent successfully; otherwise, false.</returns>
        public bool Send(String number, String message)
        {
            try
            {
                bool result = false;
                StopListener();
                ExecCommandAT("AT+CMGS=\"" + number + "\"");
                message += char.ConvertFromUtf32(26); // Ctrl-Z
                if (ExecCommandAT(message).Contains("OK"))
                    result = true;
                StartListener();

                return result;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// Sends multiple messages to multiple phone numbers.
        /// </summary>
        /// <param name="ListSMS">The list of messages and phone numbers that you want to send.</param>
        public void Send(List<SMS> ListSMS)//*****Need to test more
        {
            try
            {
                StopListener();
                ExecCommandAT("AT+CMMS=2");//can be 1
                foreach (SMS i in ListSMS)
                {
                    ExecCommandAT("AT+CMGS=\"" + i.Number + "\"");
                    i.Content += char.ConvertFromUtf32(26); // Ctrl-Z
                    if (ExecCommandAT(i.Content).Contains("OK"))
                    {
                        i.Status = true;
                        i.Date = System.DateTime.Now;
                    }
                    else
                        i.Status = false;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                StartListener();
            }
        }
        /// <summary>
        /// Gets the lists of ports are connected to computer.
        /// </summary>
        /// <returns>Lists of string which is port's name</returns>
        public static string[] GetListPort()
        {
            try
            {
                string[] listPort = SerialPort.GetPortNames();
                return listPort;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            // read response
            try
            {
                String buffer = "";
                do
                {
                    buffer += port.ReadExisting();
                }
                while (!buffer.EndsWith("\r\n"));
                String data = buffer.Trim();

                // have a new message
                if (data.StartsWith("+CMTI") && listening == true)
                {
                    StopListener();
                    data = ExecCommandAT("AT+CMGL=\"ALL\"");
                    if ((data.Contains("REC")))
                    {
                        DeleteAll();
                    }

                    //Delegate receive new message
                    //List<SMS> l = new List<SMS>();
                    List<SMS> l;
                    l = seperateString.parsestring(data);//Split new message string
                    if (NewMessageReceived != null)
                        NewMessageReceived(l);
                    StartListener();
                }
                else
                    if (data.StartsWith("+CUSD"))//request prepaid balance
                    {
                        //port.DiscardInBuffer();
                        string s = data;
                        s = s.TrimStart("+CUSD: 2,\"".ToCharArray());
                        s = s.TrimEnd("\",15".ToCharArray());
                        //data	"+CUSD: 2,\"TK chinh 10,200VND\nTK thuong 0VND\nTK ty phu noi mang 992,170,000VND\n(QC)SMS Khong Gioi Han chi voi 1000d/ngay\",15"	string
                        BalanceRequest(s);
                    }
                    else
                        if (data.StartsWith("+CLIP"))//have an incoming call
                        {
                            ExecCommandAT("ATH");
                        }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        //====================================================================================
        #region Support Method
        private void ConfigureModem()
        {
            try
            {
                ExecCommandAT("AT+CMGF=1");//set text mode
                modemName = seperateString.Modem_Name(ExecCommandAT("AT+CGMI"));
                //simSerie = seperateString.Sim_Serie(ExecCommandAT("AT+CCID"));
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// Gets a value indicating the connection status of the modem.
        /// </summary>
        /// <returns><c>true</c> if connected; otherwise, <c>false</c></returns>
        public bool IsConnect()
        {
            try
            {
                bool result = false;
                StopListener();
                if (ExecCommandAT("AT").Contains("OK"))
                    result = true;
                StartListener();
                return result;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// Disconnect from the modem
        /// </summary>
        public void Disconnect()
        {
            try
            {
                if (port.IsOpen)
                {
                    port.DiscardInBuffer();
                    port.DiscardOutBuffer();
                    port.Close();
                    port.Dispose();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// Deletes all read message in the sim.
        /// </summary>
        public void DeleteAll()
        {
            try
            {
                ExecCommandAT("AT+CMGD=1,1");
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        void StartListener()
        {
            port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
            canCheckBalance = true;
        }
        void StopListener()
        {
            port.DataReceived -= new SerialDataReceivedEventHandler(port_DataReceived);
            canCheckBalance = false;
        }
        /// <summary>
        /// Checks the balance of sim.
        /// </summary>
        /// <param name="type">The type of balance you want to check.
        /// e.g: *101#, *102#.
        /// </param>
        public void CheckBalance(string type)
        {
            try
            {
                //port.DiscardInBuffer();
                if (canCheckBalance)
                {
                    //if (type == 1)
                    //    port.Write("AT+CUSD=1," + type +",15\r");
                    //else
                    //    port.Write("AT+CUSD=1,\"*102#\",15\r");
                    port.Write("AT+CUSD=1," + type + ",15\r");
                }
                else
                    throw new Exception("Modem is sending message. Please, check balance later.");
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion
        
        //====================================================================================
        #region Communicate with modem method
        String ExecCommandAT(String cmd)
        {
            try
            {
                SendCommand(cmd);
                return ReadResponse();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        void SendCommand(String cmd)//Send a command to modem
        {
            try
            {
                port.Write(cmd + "\r");
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        String ReadResponse()//Read response from modem
        {
            try
            {
                String buffer = "";
                do
                {
                    buffer += port.ReadExisting();
                }
                while (!buffer.EndsWith("\r\nOK\r\n") && !buffer.EndsWith("\r\n> ") && !buffer.EndsWith("\r\nERROR\r\n"));
                return buffer.Trim();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        //====================================================================================
        #region Others
        
        /// <summary>
        /// Represents the method that will handle the message received from modem.
        /// </summary>
        /// <param name="l">The lists of string are new received message.</param>
        public delegate void NewMessageReceivedEventHandler(List<SMS> list);
        /// <summary>
        /// Occurs when [new message received].
        /// </summary>
        public event NewMessageReceivedEventHandler NewMessageReceived;
        /// <summary>
        /// Represents the method that will handle the string message about the balance of the sim.
        /// </summary>
        /// <param name="s">The string is about sim's balance.</param>
        public delegate void BalanceRequestEventHandler(string result);
        /// <summary>
        /// Occurs when [balance request].
        /// </summary>
        public event BalanceRequestEventHandler BalanceRequest;
        #endregion
    }

    /// <summary>
    /// A class to hold the purpose of SmsControl class
    /// </summary>
    public class SMS
    {
        string content;
        string number;
        DateTime date = new DateTime();
        bool status;
        /// <summary>
        /// Gets or sets the content.
        /// </summary>
        /// <value>
        /// The content of the message.
        /// </value>
        public string Content
        {
            set
            {
                content = value;
            }
            get
            {
                return content;
            }
        }
        /// <summary>
        /// Gets or sets the phone number.
        /// </summary>
        /// <value>
        /// The phone number.
        /// </value>
        public string Number
        {
            set
            {
                number = value;
            }
            get
            {
                return number;
            }
        }
        /// <summary>
        /// Gets or sets the date.
        /// </summary>
        /// <value>
        /// The date and time the message have been sent.
        /// </value>
        public DateTime Date
        {
            set
            {
                date = value;
            }
            get
            {
                return date;
            }
        }
        /// <summary>
        /// Gets or Sets the value that determines the message is sent.
        /// </summary>
        /// <value>
        /// The status.
        /// </value>
        public bool Status
        {
            set
            {
                status = value;
            }
            get
            {
                return status;
            }
        }
    }
}
